package rwt.controlpanel;

import java.util.*;
import java.util.concurrent.*;

import javax.swing.SwingWorker;

import rwt.controlpanel.ServerStateChangedEvent.ServerState;

/**
 * Continually monitors for a running RWT web server instance, and stores the PID when it is running. Enables
 * the "Server status" in the control panel to be updated in real time.
 */
public class WebServerMonitor extends SwingWorker<Void, ServerState>
{
	private static final long INTERVAL = 3000; // ms
	private static final long CHECK_SOON_DELAY = 80; // ms
	
	private List<ServerStateChangeListener> listeners = new ArrayList<>(2);
	
	private ServerState currentState = ServerState.STOPPED;
	private volatile int serverPid;
	
	private Semaphore waitingSemaphore = new Semaphore(1);
	
	@Override
	protected Void doInBackground()
	{
		waitingSemaphore.drainPermits();
		
		while (true)
		{
			if (isCancelled())
				break;
			
			int pid = Util.getServerPid();
			serverPid = pid;
			
			if (isCancelled())
				break;
			
			ServerState newState = ServerState.STOPPED;
			if (pid > 0)
				newState = ServerState.RUNNING;
			if (newState != currentState)
			{
				System.out.println("Server state has changed to " + newState);
				if (newState == ServerState.RUNNING)
					System.out.println("  [PID " + pid + "]");
				
				// Server state has changed since last check - report it!
				currentState = newState;
				publish(currentState);
			}
			
			try
			{
				waitingSemaphore.drainPermits();
				waitingSemaphore.tryAcquire(INTERVAL, TimeUnit.MILLISECONDS);
			}
			catch (InterruptedException e)
			{
				System.err.println("Thread interrupted in doInBackground().");
			}
		}
		
		return null;
	}
	
	public int getServerPid()
	{
		return serverPid;
	}
	
	public void checkSoon()
	{
		// Wait a little while, then release the background waitingSemaphore so that
		// we check for a running server
		new Thread(new Runnable() {
			@Override
			public void run()
			{
				try
				{
					Thread.sleep(CHECK_SOON_DELAY);
					waitingSemaphore.release();
				}
				catch (InterruptedException e)
				{
					System.err.println("Thread interrupted in checkSoon().");
				}
			}
		}).start();
	}
	
	public boolean isRunning()
	{
		return (currentState == ServerState.RUNNING);
	}
	
	@Override
	protected void process(List<ServerState> chunks)
	{
		if (chunks == null || chunks.isEmpty())
			return;
		
		// Get most recent (up-to-date) published server state
		ServerState state = chunks.get(chunks.size() - 1);
		ServerStateChangedEvent event = new ServerStateChangedEvent(state);
		
		for (ServerStateChangeListener listener : listeners)
			listener.onServerStateChanged(event);
	}
	
	public void addListener(ServerStateChangeListener listener)
	{
		listeners.add(listener);
	}
}
